#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<unistd.h>
#include<cstdlib>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

const int envnum = 64;
const int baseSize = 1024;
const int argvnum = 64; //默认argv有64个
//全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;
// 由于未来还要更新环境变量 因此最好设置为全局
char pwd[baseSize];
char pwdenv[baseSize];
//我的系统环境变量
char* genv[envnum];
int lastcode = 0; 
string GetUser()
{
   string user = getenv("USER");
   return user.empty() ? "None" : user ;
}

string GetHostName()
{
  string HostName = getenv("HOSTNAME");
  return HostName.empty() ? "None" : HostName;
}

string GetPwd()
{
  //  string pwd = getenv("PWD");
  if(nullptr == getcwd(pwd,sizeof(pwd))) return "None";
  snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
  putenv(pwdenv);
  return pwd;
}

string MakeCommandLine()
{
    char commandLine[baseSize]; 
    snprintf(commandLine,baseSize,"[%s@%s %s]# ",GetUser().c_str(),GetHostName().c_str(),GetPwd().c_str());
    return commandLine;  
}

void  PrintCommandLine() //1.命令行提示符
{
  printf("%s",MakeCommandLine().c_str());
 //刷新
 fflush(stdout);
}

bool GetCommandLine(char command_buffer[],int size)
{
   char* reasult = fgets(command_buffer,size,stdin);
   if(!reasult)
   {
     return false; //获取失败
   } 
   //处理'\n'
   command_buffer[strlen(command_buffer)-1] = 0;
   if(strlen(command_buffer) == 0) return false;
   return true;
}

bool ParaseCommandLine(char command_buffer[],int len)
{
  (void)len;
  //由于解析不断,因此为了安全最好清空表
  memset(gargv,0,sizeof(gargv));
  gargc = 0;
  //分割符
  const char* div = " ";
  gargv[gargc++] = strtok(command_buffer,div);//第一次分割
  //后续切割
  while((bool)(gargv[gargc++] = strtok(nullptr,div)));
  //最后等于null时还+一次
  gargc--;
}

void debug()
{
  printf("argc : %d\n",gargc);
  for(int i = 0 ; gargv[i];i++)
  {
    printf("argv[%d] : %s\n",i,gargv[i]);
  }
}

bool ExexcuteCommandLine()
{
  //创建子进程
  pid_t id = fork();
  if(id < 0) return false;
  else if(id == 0)
  {
    //执行命令
     execvpe(gargv[0],gargv,genv);
     //退出 成功程序替换就不会执行到exit
    exit(1);
  }
  //father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    if(WIFEXITED(status))
    {
       lastcode = WEXITSTATUS(status);
    }
    else 
    {
      lastcode = 100;
    }
    return true;
  } 
  return false;
}

void AddEnv(const char* item)
{
  int index = 0;
  while(genv[index])
  {
     index++;
  }
  //注意这里不能直接item 因为如果item在栈上开辟释放后
  //就找不到环境变量,我们需要另辟空间
  genv[index] = (char*)malloc(strlen(item)+1);
  strncpy(genv[index],item,strlen(item)+1);
  genv[++index] = nullptr;
}

//shell自己执行命令，本质是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{
  if(strcmp(gargv[0],"cd") == 0)
  {
     if(gargc == 2)
     {
       chdir(gargv[1]);
     }
     else 
     {
       lastcode = 1;
     }
     return true;
  }
  else if(strcmp(gargv[0],"export") == 0)
  {
    if(gargc == 2)
    {
        AddEnv(gargv[1]);
    }
    else 
    {
      lastcode = 2;
    }
    return true; 
  }
  else if(strcmp(gargv[0],"env") == 0)
  {
    for(int i = 0 ; genv[i] ; i++)
    {
      printf("%s\n",genv[i]);
    }
    lastcode  = 0;
    return true;
  }
  return false; 
}


void InitEnv()
{
  extern char** environ;
  int index = 0;
  //构建表
  while(environ[index])
  {
    genv[index] = (char*)malloc(strlen(environ[index])+1);
    strncpy(genv[index],environ[index],strlen(environ[index])+1);
    index++;
  }
  genv[index] = nullptr;
}

int main()
{
  InitEnv();
  char command_buffer[baseSize];
  while(true)
  {
    PrintCommandLine(); //1.命令行提示符
   // printf("\n");
   // sleep(1);    
   if(! GetCommandLine(command_buffer,baseSize)) //2.获取用户命令
   {
      continue;//获取失败 重新获取
   }
   ParaseCommandLine(command_buffer,strlen(command_buffer)); //3.分析用户命令
  // debug();
  // 检测是否为内建命令
   if(CheckAndExecBuiltCommand())
   {
     continue;     
   }
   ExexcuteCommandLine(); //4.执行命令
 
  }
 


  return 0;
}
